Skip to content

Viklin#21

Closed
viktorlindell12 wants to merge 5 commits intomainfrom
viklin
Closed

Viklin#21
viktorlindell12 wants to merge 5 commits intomainfrom
viklin

Conversation

@viktorlindell12
Copy link

@viktorlindell12 viktorlindell12 commented Nov 14, 2025

Summary by CodeRabbit

  • New Features

    • Redesigned UI with message composer, message list for real-time messaging, and updated welcome text
    • File attachment support to send files from the UI
    • Real-time send/receive messaging enabled
  • Tests

    • Added unit and integration tests covering sending, file uploads, incoming message handling, and UI interactions
  • Chores

    • Project module and build updates; added runtime/test libraries for HTTP, environment config, and JSON handling

@coderabbitai
Copy link

coderabbitai bot commented Nov 14, 2025

Walkthrough

Adds an ntfy messaging API and HTTP implementation, integrates it into the JavaFX model/controller and FXML, adds file-send support, Builder/Singleton examples, Jackson/Dotenv/WireMock dependencies and tests, plus a stylesheet and module updates.

Changes

Cohort / File(s) Summary
Build & Modules
pom.xml, src/main/java/module-info.java
Added property jackson.version=2.19.2; added dependencies: io.github.cdimascio:dotenv-java:3.2.0, org.wiremock:wiremock:4.0.0-beta.15 (test scope), Jackson artifacts (jackson-annotations, jackson-core, jackson-databind) and reordered dependency blocks; minor XML/indentation tweaks. Module declaration updated to require dotenv, java.net.http, javafx.graphics, and com.fasterxml.jackson.databind.
Messaging API & Impl
src/main/java/com/example/NtfyConnection.java, src/main/java/com/example/NtfyConnectionImpl.java, src/main/java/com/example/NtfyMessageDto.java
New NtfyConnection interface (send, receive, sendFile); NtfyConnectionImpl HTTP client implementation (env/HOST_NAME via Dotenv, sync/async POSTs, streaming GET to parse JSON events, sendFile with Title header); NtfyMessageDto record for JSON mapping (Jackson).
Model & Controller
src/main/java/com/example/HelloModel.java, src/main/java/com/example/HelloController.java
HelloModel now holds ObservableList, message state, sendMessage/sendFile/receiveMessages methods with Platform.runLater for UI updates; HelloController initializes model with NtfyConnectionImpl, binds UI components (ListView, TextField) and adds send/attach handlers.
UI Resources & Styles
src/main/resources/com/example/hello-view.fxml, src/main/resources/css/style.css
FXML root changed from StackPane to VBox; added input TextField, Send and Attach buttons, and ListView for messages; label text/style updated. New CSS file added (background and outlined-text rules; contains minor typos).
Builder & Singleton Examples
src/main/java/com/example/ManyParameters.java, src/main/java/com/example/ManyParametersBuilder.java, src/main/java/com/example/Singelton.java
Added ManyParameters and fluent ManyParametersBuilder demonstrating builder-to-constructor creation; added eager Singelton example class.
Tests & Test Doubles
src/test/java/com/example/HelloModelTest.java, src/test/java/com/example/NtfyConnectionSpy.java
New HelloModelTest suite with unit tests and WireMock integration for POST assertions; NtfyConnectionSpy test double to capture sends/files and simulate incoming messages.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Controller
    participant Model
    participant Connection
    participant Ntfy

    User->>Controller: sendMessage()
    Controller->>Controller: read inputMessage
    Controller->>Model: setMessageToSend(text)
    Controller->>Model: sendMessage()
    Model->>Connection: send(message)
    Connection->>Ntfy: POST /mytopic
    Ntfy-->>Connection: 200 OK
    Connection-->>Model: success
    Model->>Model: clear messageToSend
    Controller->>Controller: clear inputMessage

    Note over Model,Connection: receiveMessages subscription
    Model->>Connection: receive(messageHandler)
    Connection->>Ntfy: GET /mytopic/json (stream)
    Ntfy-->>Connection: stream of JSON lines
    Connection->>Connection: parse & filter event="message"
    Connection->>Model: messageHandler(NtfyMessageDto)
    Model->>Model: append to messages
Loading
sequenceDiagram
    participant User
    participant Controller
    participant FileChooser
    participant Model
    participant Connection
    participant Ntfy

    User->>Controller: attachFile()
    Controller->>FileChooser: open
    FileChooser-->>Controller: selected File
    Controller->>Model: sendFile(file)
    Model->>Connection: sendFile(filename, bytes)
    Connection->>Ntfy: POST /mytopic (binary, Title header)
    Ntfy-->>Connection: 200 OK
    Connection-->>Model: return
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Inspect NtfyConnectionImpl: HTTP streaming, JSON parsing, async handling, and exception/logging strategy.
  • Verify HelloModel.receiveMessages lifecycle and Platform.runLater usage.
  • Check FXML bindings, controller field injection, and ListView cell rendering expectations.
  • Review tests: WireMock setup/assertions and NtfyConnectionSpy state/reset semantics.
  • Confirm pom.xml property ordering and module-info compatibility.

"🐰
I hop on keys with nimble paws,
bindings bound and HTTPs laws,
messages hop from box to stream,
builders, spies — a coder's dream! 🥕"

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title check ⚠️ Warning The title 'Viklin' appears to be a branch name or abbreviated reference rather than a descriptive summary of the changes made in this pull request. Replace the title with a clear, descriptive summary of the main changes, such as 'Add NtfyConnection interface and implementation with messaging UI' or similar, reflecting the primary changes introduced.
Docstring Coverage ⚠️ Warning Docstring coverage is 6.90% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 14

🧹 Nitpick comments (10)
src/main/java/com/example/ManyParametersBuilder.java (1)

15-18: Consider validation for timeout and size parameters.

The builder accepts any integer value for timeout and size, including negative values. Depending on your domain model, negative values may not be semantically valid.

If negative values should be rejected, add validation:

 public ManyParametersBuilder setTimeout(int timeout) {
+    if (timeout < 0) {
+        throw new IllegalArgumentException("timeout cannot be negative");
+    }
     this.timeout = timeout;
     return this;
 }

 public ManyParametersBuilder setSize(int size) {
+    if (size < 0) {
+        throw new IllegalArgumentException("size cannot be negative");
+    }
     this.size = size;
     return this;
 }

Also applies to: 25-28

src/main/resources/css/style.css (1)

9-9: Add missing semicolon for consistency.

Line 9 is missing a semicolon after the property value. While this may work in some parsers, it's best practice to include it for consistency.

Apply this diff:

-    -fx-stroke-width: 1px
+    -fx-stroke-width: 1px;
src/main/java/com/example/HelloController.java (2)

38-42: Replace Swedish comments with English.

The code contains Swedish comments (lines 38, 42). For international collaboration and maintainability, please use English for all code comments.

Apply this diff:

-        // Modellens API kräver setMessageToSend() + sendMessage()
+        // Model API requires setMessageToSend() + sendMessage()
         model.setMessageToSend(text);
         model.sendMessage();
 
-        // Rensa input-fältet (matchar även testförväntningarna)
+        // Clear input field (matches test expectations)
         inputMessage.clear();

34-53: Consider adding error handling for send operations.

Both sendMessage() and attachFile() methods lack error handling. If the network request fails or file reading fails, the user receives no feedback. Consider wrapping these operations in try-catch blocks and displaying error messages to the user.

src/main/java/com/example/Singelton.java (1)

3-14: Remove the unused Singelton class to keep the codebase clean.

Verification confirms the class has no external usages—all occurrences are internal to the class definition itself (constructor, static initializer, return type). This class should be removed.

src/main/java/com/example/NtfyConnection.java (1)

15-20: Consider adding lifecycle management methods.

The receive() method appears to establish a long-lived connection, but there's no corresponding method to stop receiving or close the connection. Consider adding a close() or stop() method to properly manage resources.

src/main/java/com/example/NtfyConnectionImpl.java (2)

62-62: Remove debug statement from production code.

The peek(System.out::println) should be removed or replaced with proper logging.


16-27: Add input validation and consider HTTP client configuration.

Consider the following improvements:

  1. Validate hostName is not null/empty in the constructor
  2. Configure timeouts on the HttpClient to prevent indefinite hangs
  3. Consider making the HttpClient configurable for testing

Example configuration:

private final HttpClient http = HttpClient.newBuilder()
        .connectTimeout(Duration.ofSeconds(10))
        .build();

public NtfyConnectionImpl(String hostName) {
    if (hostName == null || hostName.isBlank()) {
        throw new IllegalArgumentException("hostName must not be null or blank");
    }
    this.hostName = hostName;
}
src/main/java/com/example/HelloModel.java (2)

56-63: Improve error handling in sendFile.

Simply calling e.printStackTrace() provides poor error visibility and user experience. Consider:

  1. Logging the error properly
  2. Showing user-friendly error notification via the UI
  3. Returning or throwing to allow the caller to handle the error

45-52: Consider validating before clearing messageToSend.

Currently, messageToSend is always cleared even if the message wasn't sent (when it's blank). While this may be intentional, consider whether you want to preserve the field value if the send is skipped.

Example alternative:

 public void sendMessage() {
     if (messageToSend != null && !messageToSend.isBlank()) {
         connection.send(messageToSend);
+        messageToSend = "";
     }
-
-    // Fältet ska tömmas efter försök att skicka
-    messageToSend = "";
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 21c51e8 and 65d75ca.

⛔ Files ignored due to path filters (1)
  • src/main/resources/background.png is excluded by !**/*.png
📒 Files selected for processing (14)
  • pom.xml (3 hunks)
  • src/main/java/com/example/HelloController.java (1 hunks)
  • src/main/java/com/example/HelloModel.java (1 hunks)
  • src/main/java/com/example/ManyParameters.java (1 hunks)
  • src/main/java/com/example/ManyParametersBuilder.java (1 hunks)
  • src/main/java/com/example/NtfyConnection.java (1 hunks)
  • src/main/java/com/example/NtfyConnectionImpl.java (1 hunks)
  • src/main/java/com/example/NtfyMessageDto.java (1 hunks)
  • src/main/java/com/example/Singelton.java (1 hunks)
  • src/main/java/module-info.java (1 hunks)
  • src/main/resources/com/example/hello-view.fxml (1 hunks)
  • src/main/resources/css/style.css (1 hunks)
  • src/test/java/com/example/HelloModelTest.java (1 hunks)
  • src/test/java/com/example/NtfyConnectionSpy.java (1 hunks)
🧰 Additional context used
🪛 Biome (2.1.2)
src/main/resources/css/style.css

[error] 2-2: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 3-3: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 6-6: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 7-7: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 8-8: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)


[error] 9-9: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)

🔇 Additional comments (9)
src/test/java/com/example/HelloModelTest.java (1)

15-84: LGTM! Comprehensive test coverage.

The test suite provides excellent coverage:

  • Delegation verification via spy
  • Integration testing with WireMock
  • Initialization state validation
  • Input field clearing behavior
  • Blank message validation

The tests are well-structured, clearly named, and use appropriate assertions.

src/main/resources/com/example/hello-view.fxml (1)

9-28: LGTM! Clean UI layout with proper controller wiring.

The FXML layout is well-structured:

  • All fx:id attributes match the corresponding @FXML fields in HelloController
  • Action handlers (#sendMessage, #attachFile) are correctly referenced
  • Layout uses appropriate spacing, alignment, and sizing
  • Comments provide clear documentation
src/main/resources/css/style.css (1)

2-2: Background image file verified.

The background.png file exists at src/main/resources/background.png, which correctly resolves from the CSS file's relative path reference.

pom.xml (2)

53-57: Maven coordinates for jackson-databind 3.0.1 are correct.

The groupId tools.jackson.core with artifactId jackson-databind and version 3.0.1 are the correct Maven coordinates for Jackson 3.0.1. No changes needed.


58-63: Consider verifying whether WireMock 4.0.0-beta.15 was a deliberate choice.

The latest stable WireMock release is 3.13.1 (released June 12, 2025), while the dependency uses 4.0.0-beta.15. If the beta version is intentional (e.g., for early adoption or forward compatibility), document that rationale. Otherwise, downgrade to 3.13.1 for production stability. Since this is a test dependency, the risk is lower, but stable versions are generally preferred.

src/main/java/com/example/NtfyMessageDto.java (1)

3-3: The review comment is based on an incorrect assumption about Jackson 3.x package naming.

Jackson 3.x annotations remain in com.fasterxml.jackson.annotation, not moved to tools.jackson. Only other Jackson packages (like databind) were renamed to tools.jackson.*. The import com.fasterxml.jackson.annotation.JsonIgnoreProperties is correct for Jackson 3.0.1, and jackson-databind 3.0.1 transitively depends on jackson-annotations, so the compilation will succeed without modification.

Likely an incorrect or invalid review comment.

src/main/java/com/example/NtfyConnection.java (1)

5-29: LGTM! Clean interface design.

The interface provides a clear abstraction for the ntfy connection with appropriate method signatures. The Swedish Javadoc is consistent throughout.

src/test/java/com/example/NtfyConnectionSpy.java (1)

7-51: LGTM! Well-designed test spy.

The spy implementation provides good test utilities with simulateIncomingMessage() and reset() methods. Public fields are acceptable for test doubles where inspection is needed.

src/main/java/com/example/HelloModel.java (1)

68-70: LGTM! Proper JavaFX thread handling.

Good use of Platform.runLater() to ensure observable list modifications happen on the JavaFX Application Thread.

public class HelloController {

private final HelloModel model = new HelloModel();
private final HelloModel model = new HelloModel(new NtfyConnectionImpl());
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

NtfyConnectionImpl requires a base URL parameter.

The NtfyConnectionImpl is instantiated without arguments, but based on the test at line 33 in HelloModelTest.java, it requires a base URL parameter: new NtfyConnectionImpl(baseUrl). This will cause a compilation error.

Please provide the base URL, potentially reading it from environment variables using the dotenv library that was added to dependencies.

Apply this diff to fix:

+import io.github.cdimascio.dotenv.Dotenv;
+
 public class HelloController {
 
-    private final HelloModel model = new HelloModel(new NtfyConnectionImpl());
+    private final Dotenv dotenv = Dotenv.load();
+    private final HelloModel model = new HelloModel(
+        new NtfyConnectionImpl(dotenv.get("NTFY_BASE_URL", "https://ntfy.sh"))
+    );
🤖 Prompt for AI Agents
In src/main/java/com/example/HelloController.java around line 14, the HelloModel
is being constructed with new NtfyConnectionImpl() but NtfyConnectionImpl
requires a base URL parameter; update the code to initialize dotenv
(Dotenv.load()) and read the base URL (e.g. Dotenv.load().get("NTFY_BASE_URL")),
provide that value to new NtfyConnectionImpl(baseUrl) when constructing
HelloModel, and supply a sensible fallback (or throw a clear error) if the
environment variable is missing.

Comment on lines +5 to +8
public ManyParameters(String computerName, int timeout,
String method, int size, byte[] data) {

}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Constructor ignores all parameters.

The constructor accepts five parameters but the body is empty - none of the values are stored. This makes the ManyParameters object useless as it cannot retain any data passed to it.

Add fields and store the parameters:

 public class ManyParameters {
+    private final String computerName;
+    private final int timeout;
+    private final String method;
+    private final int size;
+    private final byte[] data;

     public ManyParameters(String computerName, int timeout,
                           String method, int size, byte[] data) {
-
+        this.computerName = computerName;
+        this.timeout = timeout;
+        this.method = method;
+        this.size = size;
+        this.data = data != null ? data.clone() : null;
     }
+
+    // Add getters as needed
+    public String getComputerName() { return computerName; }
+    public int getTimeout() { return timeout; }
+    public String getMethod() { return method; }
+    public int getSize() { return size; }
+    public byte[] getData() { return data != null ? data.clone() : null; }
 }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/main/java/com/example/ManyParameters.java around lines 5 to 8, the
constructor accepts parameters but its body is empty so none of the values are
stored; add private fields for computerName (String), timeout (int), method
(String), size (int) and data (byte[]) to the class and assign each constructor
parameter to the corresponding field, making sure to defensively copy the byte[]
(e.g., this.data = data == null ? null : data.clone()) rather than storing the
incoming reference directly.

}


static void main() {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Incorrect main method signature.

The main() method has the wrong signature to serve as a Java entry point. It's missing the String[] parameter and public modifier.

Apply this diff to fix the signature:

-    static void main() {
+    public static void main(String[] args) {
         ManyParametersBuilder builder = new ManyParametersBuilder();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
static void main() {
public static void main(String[] args) {
ManyParametersBuilder builder = new ManyParametersBuilder();
🤖 Prompt for AI Agents
In src/main/java/com/example/ManyParameters.java around line 11, the Java entry
point method currently reads "static void main()" which has the wrong signature;
change it to the standard public static void main(String[] args) so the JVM can
recognise and invoke it as the program entry point. Ensure the method is
declared public, remains static, returns void, and accepts a single parameter of
type String[] named args.

Comment on lines +12 to +17
ManyParametersBuilder builder = new ManyParametersBuilder();
builder
.setComputerName("localhost") //Fluent API
.setTimeout(10)
.setSize(0)
.createManyParameters();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Builder result is unused and incomplete.

The createManyParameters() result is not assigned or used, and the example doesn't demonstrate setMethod() despite it being available in the builder.

Apply this diff to complete the demonstration:

     public static void main(String[] args) {
-        ManyParametersBuilder builder = new ManyParametersBuilder();
-        builder
+        ManyParameters params = new ManyParametersBuilder()
                 .setComputerName("localhost")   //Fluent API
                 .setTimeout(10)
+                .setMethod("GET")
                 .setSize(0)
                 .createManyParameters();
+        // Use params as needed
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
ManyParametersBuilder builder = new ManyParametersBuilder();
builder
.setComputerName("localhost") //Fluent API
.setTimeout(10)
.setSize(0)
.createManyParameters();
ManyParameters params = new ManyParametersBuilder()
.setComputerName("localhost") //Fluent API
.setTimeout(10)
.setMethod("GET")
.setSize(0)
.createManyParameters();
// Use params as needed
🤖 Prompt for AI Agents
In src/main/java/com/example/ManyParameters.java around lines 12 to 17, the
builder invocation currently calls createManyParameters() but doesn't store or
use the returned object and omits using the available setMethod() call; update
the snippet to call setMethod(...) (e.g., "POST"), adjust any placeholder values
as needed (e.g., setSize to a non-zero value if appropriate), assign the result
of createManyParameters() to a variable like ManyParameters params, and use or
return that variable so the example demonstrates building and consuming the
object.

Comment on lines +3 to +8
public class ManyParametersBuilder {
private String computerName;
private int timeout = 0;
private String method;
private int size = 0;
private byte[] data = null;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Builder allows construction with uninitialized required fields.

The builder permits creating a ManyParameters instance without setting computerName or method, which will be null. This could lead to NullPointerException in downstream code that expects these values.

Consider either:

  1. Making these fields required in a constructor:
 public class ManyParametersBuilder {
     private String computerName;
     private int timeout = 0;
     private String method;
     private int size = 0;
     private byte[] data = null;
+
+    public ManyParametersBuilder(String computerName, String method) {
+        this.computerName = computerName;
+        this.method = method;
+    }
  1. Or validate in createManyParameters():
public ManyParameters createManyParameters() {
    if (computerName == null || method == null) {
        throw new IllegalStateException("computerName and method are required");
    }
    return new ManyParameters(computerName, timeout, method, size, data);
}
🤖 Prompt for AI Agents
In src/main/java/com/example/ManyParametersBuilder.java around lines 3 to 8, the
builder currently allows creating a ManyParameters with null computerName or
method; add a validation step in the builder's createManyParameters() (or
build()) method to check if computerName or method is null and throw an
IllegalStateException with a clear message like "computerName and method are
required" before constructing ManyParameters; alternatively, make those fields
required via the builder constructor or a static builder factory that takes
computerName and method so they cannot be omitted—pick one approach and
implement the corresponding change consistently.

@@ -0,0 +1,14 @@
package com.example;

public class Singelton {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix the typo in the class name.

The class name Singelton contains a spelling error and should be Singleton.

Apply this diff to fix the typo:

-public class Singelton {
+public class Singleton {

Also update the constructor and field references accordingly:

-    private final static Singelton instance = new Singelton();
+    private final static Singleton instance = new Singleton();
     
-    private Singelton(){
+    private Singleton(){
     
     }
     
-    public static Singelton getInstance(){
+    public static Singleton getInstance(){
         return instance;
     }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/main/java/com/example/Singelton.java around line 3, the class name is
misspelled as "Singelton"; rename the class to "Singleton", update the filename
to Singleton.java, and update all internal references (constructor name, any
static/instance field declarations, usages in this file) to match the new
correct spelling; ensure any other files that import or reference
com.example.Singelton are updated to com.example.Singleton to keep compilation
consistent.

-fx-background-size: cover;
}
.outlined-text {
-fx-front-size:40px;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix the typo in the CSS property name.

Line 6 contains a typo: -fx-front-size should be -fx-font-size. This will prevent the font size from being applied correctly.

Apply this diff to fix the typo:

 .outlined-text {
-    -fx-front-size:40px;
+    -fx-font-size: 40px;
     -fx-stroke: white;
     -fx-fill: black;
     -fx-stroke-width: 1px
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
-fx-front-size:40px;
.outlined-text {
-fx-font-size: 40px;
-fx-stroke: white;
-fx-fill: black;
-fx-stroke-width: 1px
}
🧰 Tools
🪛 Biome (2.1.2)

[error] 6-6: Unknown property is not allowed.

See CSS Specifications and browser specific properties for more details.
To resolve this issue, replace the unknown property with a valid CSS property.

(lint/correctness/noUnknownProperty)

🤖 Prompt for AI Agents
In src/main/resources/css/style.css around line 6, there's a typo in the CSS
property name: `-fx-front-size` should be `-fx-font-size`; update the property
name to `-fx-font-size` so the font size is applied correctly.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/main/java/com/example/HelloModel.java (2)

60-62: Prevent multiple listener registrations.

Since receiveMessages() is public, it can be invoked multiple times, potentially registering duplicate listeners with connection.receive(...). This could result in messages being added to the list multiple times.

Add a guard to ensure the listener is registered only once:

+private boolean listeningStarted = false;
+
 public void receiveMessages() {
+    if (listeningStarted) {
+        return;
+    }
+    listeningStarted = true;
     connection.receive(m -> Platform.runLater(() -> messages.add(m)));
 }

Alternatively, redesign NtfyConnection.receive() to be idempotent or to return a subscription handle that can be checked.


41-46: Consider clearing messageToSend only after successful send.

Currently, messageToSend is cleared unconditionally (line 45), even when the message is null or blank and not sent. While this may be intentional to always reset the input field, it might be clearer to clear only after a successful send.

If you prefer to clear only on success:

 public void sendMessage() {
     if (messageToSend != null && !messageToSend.isBlank()) {
         connection.send(messageToSend);
+        messageToSend = "";
     }
-    messageToSend = "";
 }

Otherwise, document the current behavior (always clearing) to clarify the design intent.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 65d75ca and f2ce151.

📒 Files selected for processing (2)
  • src/main/java/com/example/HelloController.java (1 hunks)
  • src/main/java/com/example/HelloModel.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/com/example/HelloController.java
🔇 Additional comments (3)
src/main/java/com/example/HelloModel.java (3)

13-16: LGTM: Field declarations are well-structured.

The use of final for connection and messages prevents accidental reassignment, and initializing messages with FXCollections.observableArrayList() follows JavaFX best practices.


26-36: LGTM: Accessor methods follow JavaFX conventions.

Returning the ObservableList directly is standard practice in JavaFX to enable UI binding. The messageToSend getter and setter are straightforward and appropriate for the use case.


64-66: LGTM: Simple and correct.

The method correctly returns the greeting constant.

Comment on lines +18 to +24
public HelloModel(NtfyConnection connection) {
this.connection = connection;

messages.add(new NtfyMessageDto("init", 0, "message", "mytopic", "Initial message"));

receiveMessages();
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Don't call receiveMessages() in the constructor.

Invoking receiveMessages() before the constructor completes allows the this reference to escape. If connection.receive(...) starts an asynchronous operation or background thread, the callback may access a partially constructed HelloModel instance, leading to concurrency hazards.

Move the call outside the constructor:

 public HelloModel(NtfyConnection connection) {
     this.connection = connection;
     messages.add(new NtfyMessageDto("init", 0, "message", "mytopic", "Initial message"));
-    receiveMessages();
 }

Then, ensure the caller (e.g., HelloController) explicitly invokes receiveMessages() after constructing the model:

HelloModel model = new HelloModel(connection);
model.receiveMessages();
🤖 Prompt for AI Agents
In src/main/java/com/example/HelloModel.java around lines 18 to 24, the
constructor calls receiveMessages(), causing the this reference to escape;
remove the receiveMessages() call from the constructor and document/expect that
callers (e.g., HelloController) will call model.receiveMessages() immediately
after constructing the HelloModel instance so that no background threads or
callbacks can observe a partially constructed object.

Comment on lines +48 to +55
public void sendFile(File file) {
try {
byte[] data = Files.readAllBytes(file.toPath());
connection.sendFile(file.getName(), data);
} catch (IOException e) {
e.printStackTrace();
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add null check, handle large files, and use proper logging.

This method has several issues:

  1. Missing null check: No validation for the file parameter, risking NPE.
  2. Memory issue: Files.readAllBytes() loads the entire file into memory, which can cause OutOfMemoryError for large files.
  3. Poor error handling: printStackTrace() is an anti-pattern; use a logging framework instead.

Apply this diff:

 public void sendFile(File file) {
+    if (file == null || !file.exists() || !file.canRead()) {
+        // Log error or throw IllegalArgumentException
+        return;
+    }
     try {
         byte[] data = Files.readAllBytes(file.toPath());
         connection.sendFile(file.getName(), data);
     } catch (IOException e) {
-        e.printStackTrace();
+        // Use proper logging: logger.error("Failed to send file: " + file.getName(), e);
     }
 }

For large files, consider streaming the data instead of reading it all at once, if NtfyConnection.sendFile supports streaming APIs.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/main/java/com/example/HelloModel.java around lines 48 to 55, validate the
file parameter (null check, file.exists() and file.canRead()) and return/log an
error early if invalid; avoid Files.readAllBytes() for large files — if
NtfyConnection.sendFile has a streaming API use a buffered InputStream
(Files.newInputStream or FileInputStream wrapped in BufferedInputStream) and
stream chunks to the connection, otherwise check file.length() and refuse or
handle files above a safe threshold instead of loading into memory; replace
e.printStackTrace() with the application's logger (logger.error("message", e))
and ensure streams are closed in a try-with-resources block.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (6)
src/main/java/com/example/NtfyConnectionImpl.java (4)

33-33: Fix incorrect Cache header.

The header "Cache": "no" is non-standard. Use "Cache-Control": "no-cache" or "Cache-Control": "no-store" instead.

Apply this diff:

-                .header("Cache", "no")
+                .header("Cache-Control", "no-cache")

37-43: Blocking I/O will freeze the JavaFX UI thread.

The synchronous http.send() call blocks until the HTTP request completes. If called from a JavaFX thread, this will freeze the UI. Additionally, catching InterruptedException without restoring the interrupt status is incorrect.

Apply this diff to use async and restore interrupt status:

-        try {
-            http.send(httpRequest, HttpResponse.BodyHandlers.discarding());
-            return true;
-        } catch (IOException | InterruptedException e) {
-            e.printStackTrace();
-            return false;
-        }
+        try {
+            http.sendAsync(httpRequest, HttpResponse.BodyHandlers.discarding())
+                .thenApply(response -> true)
+                .exceptionally(e -> {
+                    System.err.println("Failed to send message: " + e.getMessage());
+                    return false;
+                });
+            return true;
+        } catch (Exception e) {
+            System.err.println("Failed to send message: " + e.getMessage());
+            return false;
+        }

Or change the return type to CompletableFuture<Boolean> to properly handle async results.


53-69: Blocking I/O will freeze the JavaFX UI thread.

The synchronous http.send() call on line 54 blocks until the HTTP stream completes. This is particularly problematic for the receive() method, which appears designed for long-polling. If called from a JavaFX thread, this will freeze the UI indefinitely. Additionally, InterruptedException should restore the interrupt status.

Replace with sendAsync() and handle the stream asynchronously:

-        try {
-            http.send(httpRequest, HttpResponse.BodyHandlers.ofLines())
-                    .body()
+        http.sendAsync(httpRequest, HttpResponse.BodyHandlers.ofLines())
+                .thenAccept(response -> response.body()
                     .map(s -> {
                         try {
                             return mapper.readValue(s, NtfyMessageDto.class);
                         } catch (IOException e) {
                             return null;
                         }
                     })
                     .filter(Objects::nonNull)
                     .filter(message -> "message".equals(message.event()))
-                    .forEach(messageHandler);
-
-        } catch (IOException | InterruptedException e) {
-            e.printStackTrace();
-        }
+                    .forEach(messageHandler))
+                .exceptionally(e -> {
+                    System.err.println("Failed to receive messages: " + e.getMessage());
+                    return null;
+                });

81-85: Blocking I/O will freeze the JavaFX UI thread.

The synchronous http.send() call blocks until the file upload completes. For large files, this will freeze the UI. Additionally, InterruptedException should restore the interrupt status.

Replace with sendAsync():

-        try {
-            http.send(request, HttpResponse.BodyHandlers.discarding());
-        } catch (IOException | InterruptedException e) {
-            e.printStackTrace();
-        }
+        http.sendAsync(request, HttpResponse.BodyHandlers.discarding())
+                .thenAccept(response -> System.out.println("File sent: " + filename))
+                .exceptionally(e -> {
+                    System.err.println("Failed to send file: " + e.getMessage());
+                    return null;
+                });
src/main/java/com/example/HelloModel.java (2)

18-24: Don't call receiveMessages() in the constructor.

Invoking receiveMessages() before the constructor completes allows the this reference to escape. If connection.receive(...) starts an asynchronous operation or background thread, the callback may access a partially constructed HelloModel instance, leading to concurrency hazards.

Move the call outside the constructor:

 public HelloModel(NtfyConnection connection) {
     this.connection = connection;
     messages.add(new NtfyMessageDto("init", 0, "message", "mytopic", "Initial message"));
-    receiveMessages();
 }

Then, ensure the caller (e.g., HelloController) explicitly invokes receiveMessages() after constructing the model:

HelloModel model = new HelloModel(connection);
model.receiveMessages();

45-52: Add null check, handle large files, and use proper logging.

This method has several issues:

  1. Missing null check: No validation for the file parameter, risking NPE.
  2. Memory issue: Files.readAllBytes() loads the entire file into memory, which can cause OutOfMemoryError for large files.
  3. Poor error handling: printStackTrace() is an anti-pattern; use a logging framework instead.

Apply this diff:

 public void sendFile(File file) {
+    if (file == null || !file.exists() || !file.canRead()) {
+        System.err.println("Invalid file: " + (file != null ? file.getName() : "null"));
+        return;
+    }
     try {
         byte[] data = Files.readAllBytes(file.toPath());
         connection.sendFile(file.getName(), data);
     } catch (IOException e) {
-        e.printStackTrace();
+        System.err.println("Failed to send file: " + e.getMessage());
     }
 }

For large files, consider streaming the data instead of reading it all at once, if NtfyConnection.sendFile supports streaming APIs, or check file.length() and refuse files above a safe threshold (e.g., 10 MB).

🧹 Nitpick comments (1)
src/main/java/com/example/NtfyConnectionImpl.java (1)

14-99: Replace printStackTrace() with proper logging.

Throughout the class (lines 41, 60, 67, 84, 97), errors are handled with printStackTrace(). In production code, use a logging framework (e.g., SLF4J, java.util.logging) for consistent error handling and log management.

Add a logger field:

private static final Logger logger = Logger.getLogger(NtfyConnectionImpl.class.getName());

Then replace e.printStackTrace() with:

logger.log(Level.SEVERE, "Error message", e);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f2ce151 and 3674be2.

📒 Files selected for processing (7)
  • pom.xml (6 hunks)
  • src/main/java/com/example/HelloModel.java (1 hunks)
  • src/main/java/com/example/ManyParameters.java (1 hunks)
  • src/main/java/com/example/NtfyConnectionImpl.java (1 hunks)
  • src/main/java/module-info.java (1 hunks)
  • src/test/java/com/example/HelloModelTest.java (1 hunks)
  • src/test/java/com/example/NtfyConnectionSpy.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/test/java/com/example/HelloModelTest.java
  • src/main/java/module-info.java
  • src/main/java/com/example/ManyParameters.java
🔇 Additional comments (5)
pom.xml (3)

62-76: Jackson dependencies properly versioned and organized.

The Jackson dependencies are correctly specified with consistent versioning via the property. Explicitly including jackson-annotations, jackson-core, and jackson-databind alongside their dependency relationships is appropriate.


19-19: Jackson version property is compatible with Java 25.

Jackson 2.19.2 is compatible with Java 25—Jackson 2.x targets JDK 8 as minimum and runs on newer JDK releases. Using a version property for dependency management is a best practice and this change is safe to proceed.


57-61: No issues found. Dependency addition is correct.

dotenv-java 3.2.0 is the latest stable version, and it is compatible with Java 25 (requires Java 11 or greater). The runtime scope is intentional—NtfyConnectionImpl uses dotenv in production code (not test-only) to load HOST_NAME from the environment at startup. The dependency and module requirement are appropriate.

src/test/java/com/example/NtfyConnectionSpy.java (2)

7-32: LGTM! Well-structured test spy.

The spy correctly implements the NtfyConnection interface with simple state capture. Public fields are appropriate for test utilities, making assertions straightforward.


34-49: Useful test helpers.

The simulateIncomingMessage() and reset() methods provide clean test setup and teardown capabilities.

Comment on lines +41 to +46
<dependency>
<groupId>org.wiremock</groupId>
<artifactId>wiremock</artifactId>
<version>4.0.0-beta.15</version>
<scope>test</scope>
</dependency>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

Verify WireMock beta version is intentional.

WireMock 4.0.0-beta.15 is a beta release. While test dependencies are lower risk, verify whether a stable release is available and should be used instead. Beta versions may introduce breaking changes or lack stability guarantees.


🌐 Web query:

What is the latest stable version of WireMock?

💡 Result:

The latest stable WireMock release is 3.13.1 (released June 12, 2025). [1][2]

Sources:
[1] WireMock community announcement for 3.13.1.
[2] WireMock website release notes / changelog (3.x series).


Use WireMock stable version 3.13.1 or document justification for beta.

Verification confirms WireMock 3.13.1 is the latest stable release. Update to the stable version unless there's a specific reason to use 4.0.0-beta.15 (e.g., testing new major version features). Even for test dependencies, stable releases are preferred.

🤖 Prompt for AI Agents
In pom.xml around lines 41 to 46, the WireMock dependency is pinned to a beta
release (4.0.0-beta.15); update the dependency to the stable release
(org.wiremock:wiremock:3.13.1) unless you have a documented reason to rely on
the beta. Modify the version element to 3.13.1 and, if you intentionally need
beta features, add a short comment in the pom or project docs explaining which
beta-only features are required and why upgrading to stable is not possible yet.

Comment on lines +88 to +98
public void sendAsync(String message) {
HttpRequest httpRequest = HttpRequest.newBuilder()
.POST(HttpRequest.BodyPublishers.ofString(message))
.header("Cache", "no")
.uri(URI.create(hostName + "/mytopic"))
.build();

http.sendAsync(httpRequest, HttpResponse.BodyHandlers.discarding())
.thenAccept(response -> System.out.println("Message sent async"))
.exceptionally(e -> { e.printStackTrace(); return null; });
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Method not part of the NtfyConnection interface.

The sendAsync method is not declared in the NtfyConnection interface and won't be called by code depending on the interface. This is likely dead code. Also, line 91 has the same incorrect "Cache": "no" header.

Either remove this method or add it to the NtfyConnection interface if it's intended for use.

🤖 Prompt for AI Agents
src/main/java/com/example/NtfyConnectionImpl.java around lines 88-98: the
sendAsync method is not declared on the NtfyConnection interface (so it will
never be called through the interface) and it uses an incorrect header key/value
("Cache": "no") on line 91; either remove this method if it's dead code or add a
corresponding sendAsync signature to the NtfyConnection interface and implement
it across all implementations, and while doing so correct the header to the
proper HTTP header (e.g., "Cache-Control": "no-cache") and ensure any callers
use the interface method rather than the concrete class.

@kappsegla kappsegla closed this Nov 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants